/////////////////////////////////////////////////////////////////
/*
MIT License

Copyright (c) 2019 lewis he

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

axp20x.cpp - Arduino library for X-Power AXP202 chip.
Created by Lewis he on April 1, 2019.
github:https://github.com/lewisxhe/AXP202X_Libraries
*/
/////////////////////////////////////////////////////////////////

#include "axp20x.h"

const uint8_t AXP20X_Class::startupParams[] = {
    0b00000000,
    0b01000000,
    0b10000000,
    0b11000000
};

const uint8_t AXP20X_Class::longPressParams[] = {
    0b00000000,
    0b00010000,
    0b00100000,
    0b00110000
};

const uint8_t AXP20X_Class::shutdownParams[] = {
    0b00000000,
    0b00000001,
    0b00000010,
    0b00000011
};

const uint8_t AXP20X_Class::targetVolParams[] = {
    0b00000000,
    0b00100000,
    0b01000000,
    0b01100000
};


// Power Output Control register
uint8_t AXP20X_Class::_outputReg;

int AXP20X_Class::begin(TwoWire &port, uint8_t addr)
{
    uint8_t chip_id;
    _i2cPort = &port; //Grab which port the user wants us to use
    _address = addr;
    _readByte(AXP202_IC_TYPE, 1, &chip_id);
    if ((chip_id & 0x0f) == 0x1) {
        AXP_DEBUG("chip id detect 0x%x\n", chip_id);
        _readByte(AXP202_LDO234_DC23_CTL, 1, &_outputReg);
        _init = true;
        return AXP202_PASS;
    } else {
        AXP_DEBUG("chip id not detect 0x%x\n", chip_id);
        return AXP202_FAIL;
    }
}

bool AXP20X_Class::isLDO2Enable()
{
    return IS_OPEN(_outputReg, AXP202_LDO2);
}

bool AXP20X_Class::isLDO3Enable()
{
    return IS_OPEN(_outputReg, AXP202_LDO3);
}

bool AXP20X_Class::isLDO4Enable()
{
    return IS_OPEN(_outputReg, AXP202_LDO4);
}

bool AXP20X_Class::isDCDC2Enable()
{
    return IS_OPEN(_outputReg, AXP202_DCDC2);
}

bool AXP20X_Class::isDCDC3Enable()
{
    return IS_OPEN(_outputReg, AXP202_DCDC3);
}

int AXP20X_Class::setPowerOutPut(uint8_t ch, bool en)
{
    uint8_t data;
    uint8_t val = 0;
    if (!_init)return AXP202_NOT_INIT;

    _readByte(AXP202_LDO234_DC23_CTL, 1, &data);
    if (en) {
        data |= (1 << ch);
    } else {
        data &= (~(1 << ch));
    }

    FORCED_OPEN_DCDC3(data);    //! Must be forced open in T-Watch

    _writeByte(AXP202_LDO234_DC23_CTL, 1, &data);
    delay(1);
    _readByte(AXP202_LDO234_DC23_CTL, 1, &val);
    if (data == val) {
        _outputReg = val;
        return AXP202_PASS;
    }
    return AXP202_FAIL;
}

bool AXP20X_Class::isChargeing()
{
    uint8_t reg;
    if (!_init)return AXP202_NOT_INIT;
    _readByte(AXP202_MODE_CHGSTATUS, 1, &reg);
    return IS_OPEN(reg, 6);
}

float AXP20X_Class::getAcinVoltage()
{
    if (!_init)return AXP202_NOT_INIT;
    return _getRegistResult(AXP202_ACIN_VOL_H8, AXP202_ACIN_VOL_L4) * AXP202_ACIN_VOLTAGE_STEP;
}

float AXP20X_Class::getAcinCurrent()
{
    if (!_init)return AXP202_NOT_INIT;
    uint8_t hv, lv;
    return _getRegistResult(AXP202_ACIN_CUR_H8, AXP202_ACIN_CUR_L4) * AXP202_ACIN_CUR_STEP;
}

float AXP20X_Class::getVbusVoltage()
{
    if (!_init)return AXP202_NOT_INIT;
    return _getRegistResult(AXP202_VBUS_VOL_H8, AXP202_VBUS_VOL_L4) * AXP202_VBUS_VOLTAGE_STEP;
}

float AXP20X_Class::getVbusCurrent()
{
    if (!_init)return AXP202_NOT_INIT;
    return _getRegistResult(AXP202_VBUS_CUR_H8, AXP202_VBUS_CUR_L4) * AXP202_VBUS_CUR_STEP;
}

float AXP20X_Class::getTemp()
{
    if (!_init)return AXP202_NOT_INIT;
    uint8_t hv, lv;
    _readByte(AXP202_INTERNAL_TEMP_H8, 1, &hv);
    _readByte(AXP202_INTERNAL_TEMP_L4, 1, &lv);
    float rslt =  hv << 8 | (lv & 0xF);
    return rslt / 1000;
}

float AXP20X_Class::getTSTemp()
{
    if (!_init)return AXP202_NOT_INIT;
    return _getRegistResult(AXP202_TS_IN_H8, AXP202_TS_IN_L4) * AXP202_TS_PIN_OUT_STEP;
}

float AXP20X_Class::getGPIO0Voltage()
{
    if (!_init)return AXP202_NOT_INIT;
    return _getRegistResult(AXP202_GPIO0_VOL_ADC_H8, AXP202_GPIO0_VOL_ADC_L4) * AXP202_GPIO0_STEP;
}

float AXP20X_Class::getGPIO1Voltage()
{
    if (!_init)return AXP202_NOT_INIT;
    return _getRegistResult(AXP202_GPIO1_VOL_ADC_H8, AXP202_GPIO1_VOL_ADC_L4) * AXP202_GPIO1_STEP;
}

/*
Note: the battery power formula:
Pbat =2* register value * Voltage LSB * Current LSB / 1000.
(Voltage LSB is 1.1mV; Current LSB is 0.5mA, and unit of calculation result is mW.)
*/
float AXP20X_Class::getBattInpower()
{
    float rslt;
    uint8_t hv, mv, lv;
    if (!_init)return AXP202_NOT_INIT;
    _readByte(AXP202_BAT_POWERH8, 1, &hv);
    _readByte(AXP202_BAT_POWERM8, 1, &mv);
    _readByte(AXP202_BAT_POWERL8, 1, &lv);
    rslt =  (hv << 16) | (mv << 8) | lv;
    rslt = 2 * rslt * 1.1 * 0.5 / 1000;
    return rslt;
}

float AXP20X_Class::getBattVoltage()
{
    if (!_init)return AXP202_NOT_INIT;
    return _getRegistResult(AXP202_BAT_AVERVOL_H8, AXP202_BAT_AVERVOL_L4) * AXP202_BATT_VOLTAGE_STEP;

}

float AXP20X_Class::getBattChargeCurrent()
{
    if (!_init)return AXP202_NOT_INIT;
    return _getRegistResult(AXP202_BAT_AVERCHGCUR_H8, AXP202_BAT_AVERCHGCUR_L4) * AXP202_BATT_CHARGE_CUR_STEP;
}

float AXP20X_Class::getBattDischargeCurrent()
{
    float rslt;
    uint8_t hv, lv;
    if (!_init)return AXP202_NOT_INIT;
    _readByte(AXP202_BAT_AVERDISCHGCUR_H8, 1, &hv);
    _readByte(AXP202_BAT_AVERDISCHGCUR_L5, 1, &lv);
    rslt = (hv << 5) | (lv & 0x1F);
    return rslt * AXP202_BATT_DISCHARGE_CUR_STEP;
}

float AXP20X_Class::getSysIPSOUTVoltage()
{
    float rslt;
    uint8_t hv, lv;
    if (!_init)return AXP202_NOT_INIT;
    _readByte(AXP202_APS_AVERVOL_H8, 1, &hv);
    _readByte(AXP202_APS_AVERVOL_L4, 1, &lv);
    rslt = (hv << 4) | (lv & 0xF);
    return rslt;
}

/*
Coulomb calculation formula:
C= 65536 * current LSB *（charge coulomb counter value - discharge coulomb counter value） /
3600 / ADC sample rate. Refer to REG84H setting for ADC sample rate；the current LSB is
0.5mA；unit of the calculation result is mAh. ）
*/
float AXP20X_Class::getBattChargeCoulomb()
{
    float rslt;
    if (!_init)return AXP202_NOT_INIT;
    return rslt;
}

float AXP20X_Class::getBattDischargeCoulomb()
{
    float rslt;
    if (!_init)return AXP202_NOT_INIT;
    return rslt;
}

int AXP20X_Class::adc1Enable(uint16_t params, bool en)
{
    if (!_init)return AXP202_NOT_INIT;
    uint8_t val;
    _readByte(AXP202_ADC_EN1, 1, &val);
    if (en)
        val |= params;
    else
        val &= ~(params);
    _writeByte(AXP202_ADC_EN1, 1, &val);

    _readByte(AXP202_ADC_EN1, 1, &val);
    return AXP202_PASS;
}

int AXP20X_Class::adc2Enable(uint16_t params, bool en)
{
    if (!_init)return AXP202_NOT_INIT;
    uint8_t val;
    _readByte(AXP202_ADC_EN2, 1, &val);
    if (en)
        val |= params;
    else
        val &= ~(params);
    _writeByte(AXP202_ADC_EN2, 1, &val);
    return AXP202_PASS;
}



int AXP20X_Class::enableIRQ(uint32_t params, bool en)
{
    if (!_init)return AXP202_NOT_INIT;
    uint8_t val, val1;
    if (params & 0xFF) {
        val1 = params & 0xFF;
        _readByte(AXP202_INTEN1, 1, &val);
        if (en)
            val |= val1;
        else
            val &= ~(val1);
        AXP_DEBUG("%s [0x%x]val:0x%x\n", en ? "enable" : "disable", AXP202_INTEN1, val);
        _writeByte(AXP202_INTEN1, 1, &val);
    }
    if (params & 0xFF00) {
        val1 = params >> 8;
        _readByte(AXP202_INTEN2, 1, &val);
        if (en)
            val |= val1;
        else
            val &= ~(val1);
        AXP_DEBUG("%s [0x%x]val:0x%x\n", en ? "enable" : "disable", AXP202_INTEN2, val);
        _writeByte(AXP202_INTEN2, 1, &val);
    }

    if (params & 0xFF0000) {
        val1 = params >> 16;
        _readByte(AXP202_INTEN3, 1, &val);
        if (en)
            val |= val1;
        else
            val &= ~(val1);
        AXP_DEBUG("%s [0x%x]val:0x%x\n", en ? "enable" : "disable", AXP202_INTEN3, val);
        _writeByte(AXP202_INTEN3, 1, &val);
    }

    if (params & 0xFF000000) {
        val1 = params >> 24;
        _readByte(AXP202_INTEN4, 1, &val);
        if (en)
            val |= val1;
        else
            val &= ~(val1);
        AXP_DEBUG("%s [0x%x]val:0x%x\n", en ? "enable" : "disable", AXP202_INTEN4, val);
        _writeByte(AXP202_INTEN4, 1, &val);
    }
    return AXP202_PASS;
}

int AXP20X_Class::readIRQ()
{
    if (!_init)return AXP202_NOT_INIT;
    for (int i = 0; i < 5; ++i) {
        _readByte(AXP202_INTSTS1 + i, 1, &_irq[i]);
    }
    return AXP202_PASS;
}

void AXP20X_Class::clearIRQ()
{
    uint8_t val = 0xFF;
    for (int i = 0; i < 5; i++) {
        _writeByte(AXP202_INTSTS1 + i, 1, &val);
    }
    memset(_irq, 0, sizeof(_irq));
}


bool AXP20X_Class::isAcinOverVoltageIRQ()
{
    return (bool)(_irq[0] & BIT_MASK(7));
}

bool AXP20X_Class::isAcinPlugInIRQ()
{
    return (bool)(_irq[0] & BIT_MASK(6));
}

bool AXP20X_Class::isAcinRemoveIRQ()
{
    return (bool)(_irq[0] & BIT_MASK(5));
}

bool AXP20X_Class::isVbusOverVoltageIRQ()
{
    return (bool)(_irq[0] & BIT_MASK(4));
}

bool AXP20X_Class::isVbusPlugInIRQ()
{
    return (bool)(_irq[0] & BIT_MASK(3));
}

bool AXP20X_Class::isVbusRemoveIRQ()
{
    return (bool)(_irq[0] & BIT_MASK(2));
}

bool AXP20X_Class::isVbusLowVHOLDIRQ()
{
    return (bool)(_irq[0] & BIT_MASK(1));
}

bool AXP20X_Class::isBattPlugInIRQ()
{
    return (bool)(_irq[1] & BIT_MASK(7));
}
bool AXP20X_Class::isBattRemoveIRQ()
{
    return (bool)(_irq[1] & BIT_MASK(6));
}
bool AXP20X_Class::isBattEnterActivateIRQ()
{
    return (bool)(_irq[1] & BIT_MASK(5));
}
bool AXP20X_Class::isBattExitActivateIRQ()
{
    return (bool)(_irq[1] & BIT_MASK(4));
}
bool AXP20X_Class::isChargingIRQ()
{
    return (bool)(_irq[1] & BIT_MASK(3));
}
bool AXP20X_Class::isChargingDoneIRQ()
{
    return (bool)(_irq[1] & BIT_MASK(2));
}
bool AXP20X_Class::isBattTempLowIRQ()
{
    return (bool)(_irq[1] & BIT_MASK(1));
}
bool AXP20X_Class::isBattTempHighIRQ()
{
    return (bool)(_irq[1] & BIT_MASK(0));
}

bool AXP20X_Class::isPEKShortPressIRQ()
{
    return (bool)(_irq[2] & BIT_MASK(1));
}

bool AXP20X_Class::isPEKLongtPressIRQ()
{
    return (bool)(_irq[2] & BIT_MASK(0));
}

bool AXP20X_Class::isVBUSPlug()
{
    if (!_init)return AXP202_NOT_INIT;
    uint8_t val;
    _readByte(AXP202_STATUS, 1, &val);
    return (bool)(_irq[2] & BIT_MASK(5));
}

int AXP20X_Class::setDCDC2Voltage(uint16_t mv)
{
    if (!_init)return AXP202_NOT_INIT;
    if (mv < 700) {
        AXP_DEBUG("DCDC2:Below settable voltage:700mV~2275mV");
        mv = 700;
    }
    if (mv > 2275) {
        AXP_DEBUG("DCDC2:Above settable voltage:700mV~2275mV");
        mv = 2275;
    }
    uint8_t val = (mv - 0.7) / 0.025;
    _writeByte(AXP202_DC2OUT_VOL, 1, &val);
    return AXP202_PASS;
}

int AXP20X_Class::setDCDC3Voltage(uint16_t mv)
{
    if (!_init)return AXP202_NOT_INIT;
    if (mv < 700) {
        AXP_DEBUG("DCDC3:Below settable voltage:700mV~3500mV");
        mv = 700;
    }
    if (mv > 3500) {
        AXP_DEBUG("DCDC3:Above settable voltage:700mV~3500mV");
        mv = 3500;
    }
    uint8_t val = (mv - 0.7) / 0.025;
    _writeByte(AXP202_DC3OUT_VOL, 1, &val);
    return AXP202_PASS;
}


int AXP20X_Class::setLDO2Voltage(uint16_t mv)
{
    if (!_init)return AXP202_NOT_INIT;
    if (mv < 1800) {
        AXP_DEBUG("LDO2:Below settable voltage:1800mV~3300mV");
        mv = 1800;
    }
    if (mv > 3300) {
        AXP_DEBUG("LDO2:Above settable voltage:1800mV~3300mV");
        mv = 3300;
    }
    uint8_t val = (mv - 1.8) / 0.1;
    _writeByte(AXP202_LDO24OUT_VOL, 1, &val);
    return AXP202_PASS;
}

//!29H LDO3 Output Voltage Setting
// LDO3 Mode select：
// Bit 7    0：LDO mode，voltage can be set by [6:0]
//          1：enable/disable control mode，and voltage is determined by LDO3IN
// 6-0      LDO3 output voltage setting Bit6-Bit0
// 0.7-2.275V，25mV/step
// Vout=[0.7+(Bit6-0)*0.025]V
int AXP20X_Class::setLDO3Voltage(uint16_t mv)
{
    if (!_init)return AXP202_NOT_INIT;
    if (mv < 700) {
        AXP_DEBUG("LDO3:Below settable voltage:700mV~2275mV");
        mv = 700;
    }
    if (mv > 2275) {
        AXP_DEBUG("LDO3:Above settable voltage:700mV~2275mV");
        mv = 2275;
    }
    uint8_t val = (mv - 0.7) / 0.025;
    val |= BIT_MASK(7);
    _writeByte(AXP202_LDO3OUT_VOL, 1, &val);
    return AXP202_PASS;
}

int AXP20X_Class::setLDO4Voltage(axp_ldo4_table_t param)
{
    if (!_init)return AXP202_NOT_INIT;
    if (param >= AXP202_LDO4_MAX)return AXP202_INVALID;
    uint8_t val;
    _readByte(AXP202_LDO24OUT_VOL, 1, &val);
    val &= 0xF0;
    val |= param;
    _writeByte(AXP202_LDO24OUT_VOL, 1, &val);
    return AXP202_PASS;
}

// 0 : LDO  1 : DCIN
int AXP20X_Class::setLDO3Mode(uint8_t mode)
{
    uint8_t val;
    _readByte(AXP202_LDO3OUT_VOL, 1, &val);
    if (mode) {
        val |= BIT_MASK(7);
    } else {
        val &= (~BIT_MASK(7));
    }
    _writeByte(AXP202_LDO3OUT_VOL, 1, &val);
    return AXP202_PASS;

}

int AXP20X_Class::setStartupTime(uint8_t param)
{
    uint8_t val;
    if (!_init)return AXP202_NOT_INIT;
    if (param > sizeof(startupParams) / sizeof(startupParams[0]))return AXP202_INVALID;
    _readByte(AXP202_POK_SET, 1, &val);
    val &= (~0b11000000);
    val |= startupParams[param];
    _writeByte(AXP202_POK_SET, 1, &val);
}

int AXP20X_Class::setlongPressTime(uint8_t param)
{
    uint8_t val;
    if (!_init)return AXP202_NOT_INIT;
    if (param > sizeof(longPressParams) / sizeof(longPressParams[0]))return AXP202_INVALID;
    _readByte(AXP202_POK_SET, 1, &val);
    val &= (~0b00110000);
    val |= longPressParams[param];
    _writeByte(AXP202_POK_SET, 1, &val);
}

int AXP20X_Class::setShutdownTime(uint8_t param)
{
    uint8_t val;
    if (!_init)return AXP202_NOT_INIT;
    if (param > sizeof(shutdownParams) / sizeof(shutdownParams[0]))return AXP202_INVALID;
    _readByte(AXP202_POK_SET, 1, &val);
    val &= (~0b00000011);
    val |= shutdownParams[param];
    _writeByte(AXP202_POK_SET, 1, &val);
}

int AXP20X_Class::setTimeOutShutdown(bool en)
{
    uint8_t val;
    if (!_init)return AXP202_NOT_INIT;
    _readByte(AXP202_POK_SET, 1, &val);
    if (en)
        val |= (1 << 3);
    else
        val &= (~(1 << 3));
    _writeByte(AXP202_POK_SET, 1, &val);
}


float AXP20X_Class::getSettingChargeCurrent()
{
    uint8_t val;
    if (!_init)return AXP202_NOT_INIT;
    _readByte(AXP202_CHARGE1, 1, &val);
    val &= 0b00000111;
    float cur = 300.0 + val * 100.0;
    AXP_DEBUG("Setting Charge current : %.2f mA\n", cur);
    return cur;
}

bool AXP20X_Class::isChargeingEnable()
{
    uint8_t val;
    if (!_init)return false;
    _readByte(AXP202_CHARGE1, 1, &val);
    if (val & (1 << 7)) {
        AXP_DEBUG("Charging enable is enable\n");
        val = true;
    } else {
        AXP_DEBUG("Charging enable is disable\n");
        val = false;
    }
    return val;
}

int AXP20X_Class::enableChargeing(bool en)
{
    uint8_t val;
    if (!_init)return AXP202_NOT_INIT;
    _readByte(AXP202_CHARGE1, 1, &val);
    val |= (1 << 7);
    _writeByte(AXP202_CHARGE1, 1, &val);
    return AXP202_PASS;
}

int AXP20X_Class::setChargingTargetVoltage(axp_chargeing_vol_t param)
{
    uint8_t val;
    if (!_init)return AXP202_NOT_INIT;
    if (param > sizeof(targetVolParams) / sizeof(targetVolParams[0]))return AXP202_INVALID;
    _readByte(AXP202_CHARGE1, 1, &val);
    val &= ~(0b01100000);
    val |= targetVolParams[param];
    _writeByte(AXP202_CHARGE1, 1, &val);
    return AXP202_PASS;
}

int AXP20X_Class::getBattPercentage()
{
    if (!_init)return AXP202_NOT_INIT;
    uint8_t val;
    _readByte(AXP202_BATT_PERCENTAGE, 1, &val);
    if (!(val & BIT_MASK(7))) {
        return val & (~BIT_MASK(7));
    }
    return 0;
}


int AXP20X_Class::debugCharging()
{
    uint8_t val;
    _readByte(AXP202_CHARGE1, 1, &val);
    AXP_DEBUG("SRC REG:0x%x\n", val);
    if (val & (1 << 7)) {
        AXP_DEBUG("Charging enable is enable\n");
    } else {
        AXP_DEBUG("Charging enable is disable\n");
    }
    AXP_DEBUG("Charging target-voltage : 0x%x\n", ((val & 0b01100000) >> 5 ) & 0b11);
    if (val & (1 << 4)) {
        AXP_DEBUG("end when the charge current is lower than 15%% of the set value\n");
    } else {
        AXP_DEBUG(" end when the charge current is lower than 10%% of the set value\n");
    }
    val &= 0b00000111;
    float cur = 300.0 + val * 100.0;
    AXP_DEBUG("Charge current : %.2f mA\n", cur);
}


int AXP20X_Class::debugStatus()
{
    if (!_init)return AXP202_NOT_INIT;
    uint8_t val, val1, val2;
    _readByte(AXP202_STATUS, 1, &val);
    _readByte(AXP202_MODE_CHGSTATUS, 1, &val1);
    _readByte(AXP202_IPS_SET, 1, &val2);
    AXP_DEBUG("AXP202_STATUS:   AXP202_MODE_CHGSTATUS   AXP202_IPS_SET\n");
    AXP_DEBUG("0x%x\t\t\t 0x%x\t\t\t 0x%x\n", val, val1, val2);
}

